baseUrl = "http://127.0.0.1:5000/";
editor_right = 370;

function loadData(url, actionFunction, in_data, async) {
    /*
    从url中加载数据，actionFunction是回调函数，回调函数中，通过httpRequest.responseText拿到服务器响应数据
     */
    $.ajax({
        type: "GET",
        url: url,
        async: async, // 默认是true
        success: function (obj) {
            actionFunction(obj, in_data);
        }
    });
}

function action1(httpRequest) {
    /**
     * @httpRequest 回调函数，通过httpRequest.responseText拿到服务器响应数据
     */
    let obj = JSON.parse(httpRequest.responseText);  // 将字符串转换为js对象
    let obj_str = JSON.stringify(obj);  // 将js对象转换为json字符串

}

function ykMenuItemClicked(jq_li) {
    /**
     * 处理按钮点击事件
     */
    // alert('标签被单击了！');
    console.log("标签被单击了！")
    hideAllMenuList();
    if ($(jq_li).text() === "温度分布图") {
        $("#editor").load("surface.html");  // 引入其他界面时，不能使用重复使用ykInit()方法
    } else if ($(jq_li).text() === "冷却塔计算") {
        $("#editor").load("cooling_tower.html");
    }
}

function open_project(list_item) {
    /**
     * 处理菜单项->打开
     */
}

function renderForceGraph(nodes, edges) {
    /**
     * 根据nodes和edges渲染力图
     */
    let r_nodes = 30;
    let arrow_length = 10;  // 箭头长度
    let arrow_width = 8;  // 箭头宽度
    let line_width = 2;
    // -----------------------------------------设置 svg 的尺寸------------------------------------------------
    let editor_div = $("#editor")
    let width = editor_div.width() - editor_right - 5;
    let height = editor_div.height() / 1.5;
    let svg = d3.select("svg");
    svg.attr("width", width);
    svg.attr("height", height);
    // -----------------------------------------设置 svg 的尺寸------------------------------------------------
    // -----------------------------------------清空 svg，准备重绘----------------------------------------------
    let g_in_svg = $("svg g");
    if (g_in_svg.length === 0) {

    } else {
        g_in_svg.remove();
        // update(nodes, edges);
        // return;
    }
    // -----------------------------------------清空 svg，准备重绘----------------------------------------------
    // -----------------------------------------绘制 svg，渲染力图----------------------------------------------
    // -----------------------------------------初始化 svg ------------------------------------------------
    g = svg.append("g")
        .attr("transform", "translate(60,60)");

    colorScale = d3.scaleOrdinal()
        .domain(d3.range(nodes.length))
        .range(d3.schemeCategory10)

    forceSimulation = d3.forceSimulation()
        .force("link", d3.forceLink())
        .force("charge", d3.forceManyBody())
        .force("center", d3.forceCenter())
    //初始化力导向图，也就是传入数据
    //生成节点数据
    forceSimulation.nodes(nodes)
        .on("tick", ticked);//这个函数很重要，后面给出具体实现和说明
    //生成边数据
    forceSimulation.force("link")
        .links(edges)
        .distance(function (d) {//每一边的长度
            return d.value * 200;
        })
    //设置图形的中心位置
    forceSimulation.force("center")
        .x(width / 3)
        .y(height / 3);
    // -----------------------------------------初始化 svg ------------------------------------------------

    // ------------------------------------------绘制边------------------------------------------------
    links = g.append("g").attr("id", "links")
        .selectAll("line")
        .data(edges)
        .join(
            enter => enter.append("line")
                .attr("stroke", (d, i) => colorScale(i))
                .attr("stroke-width", line_width),
            update => update
                .attr("stroke", (d, i) => colorScale(i))
                .attr("stroke-width", line_width),
            exit => exit.remove()
        );
    // var linksText = g.append("g")
    //     .selectAll("text")
    //     .data(edges)
    //     .enter()
    //     .append("text")
    //     .text(function (d) {
    //         return d.relation;
    //     })
    let semi_arrow_width = parseInt(arrow_width / 2);
    let arrow_path = "M0,-" + semi_arrow_width + "L" + arrow_length + ",0L0," + semi_arrow_width;
    var marker = svg.append("defs")
        .append("marker")
        .attr("id", "arrow")
        // .attr("markerUnits", "strokeWidth")  // 箭头随连线宽度变化
        .attr("markerUnits", "userSpaceOnUse")
        .attr("viewBox", "0 -" + semi_arrow_width + " " + arrow_length + " " + arrow_width)//坐标系的区域
        .attr("refX", r_nodes * 0.8 + arrow_length)//箭头坐标
        .attr("refY", 0)
        .attr("markerWidth", 12)//标识的大小
        .attr("markerHeight", 12)
        .attr("orient", "auto")//绘制方向，可设定为：auto（自动确认方向）和 角度值
        .attr("stroke-width", 3)//箭头宽度
        .append("path")
        .attr("d", arrow_path)//箭头的路径
        .attr('fill', '#000000');//箭头颜色

    //绘制节点
    //老规矩，先为节点和节点上的文字分组
    gs = g.selectAll(".nodes")
        .data(nodes)
        .join(
            enter => enter.append("g")
                .attr("transform", (d, i) => "translate(" + d.x + "," + d.y + ")")
                .attr("class", "nodes")
                .on("click", clicked)
                .call(d3.drag()
                    .on("start", started)
                    .on("drag", dragged)
                    .on("end", ended)
                ),
            update => update
                .attr("transform", (d, i) => "translate(" + d.x + "," + d.y + ")")
                .attr("class", "nodes")
                .on("click", clicked)
                .call(d3.drag()
                    .on("start", started)
                    .on("drag", dragged)
                    .on("end", ended)
                ),
            exit => exit
                .remove()
        );
    //绘制节点
    gs.append("circle")
        .attr("r", r_nodes)
        .attr("fill", function (d, i) {
            return colorScale(i);
        })
    //文字
    gs.append("text")
        .attr("x", -10)
        .attr("y", -20)
        .attr("dy", 10)
        .text(function (d) {
            return d.name;
        })

    // ------------------------------------------定义事件方法------------------------------------------------
    function ticked() {
        links
            .attr("x1", function (d) {
                return d.source.x;
            })
            .attr("y1", function (d) {
                return d.source.y;
            })
            .attr("x2", function (d) {
                return d.target.x;
            })
            .attr("y2", function (d) {
                return d.target.y;
            })
            .attr("marker-end", "url(#arrow)");

        // linksText
        //     .attr("x", function (d) {
        //         return (d.source.x + d.target.x) / 2;
        //     })
        //     .attr("y", function (d) {
        //         return (d.source.y + d.target.y) / 2;
        //     });

        gs
            .attr("transform", function (d) {
                return "translate(" + d.x + "," + d.y + ")";
            });
    }

    function started(d) {
        if (!d3.event.active) {
            forceSimulation.alphaTarget(0.8).restart();
        }
        d.fx = d.x;
        d.fy = d.y;
    }

    function dragged(d) {
        d.fx = d3.event.x;
        d.fy = d3.event.y;
    }

    function ended(d) {
        if (!d3.event.active) {
            forceSimulation.alphaTarget(0);
        }
        d.fx = null;
        d.fy = null;
    }

    function clicked(d, i, group) {
        console.log("clicked");
        let event = d3.event;
        if (event.ctrlKey) {
            // todo
            // 如果ctrl被按下，则查询该电场中的机组
            graph_node_clicked_with_control(d);
        } else {
            // todo
            // 查询d.id对应的电厂对应的报告
            graph_node_clicked(d);
        }
    }

    // ------------------------------------------定义事件方法------------------------------------------------
}


function graph_node_clicked(graph_node) {
    //处理d3力图节点的点击事件
    // 获取点击对象类型
    let sheet_data = {
        columns: ["item", "值"],
        // sort: true,
        // pagination: true,
    };
    let node_type = graph_node.type;
    let node_name = graph_node.name;
    node_id = graph_node.id;
    sheet_data["data"] = [
        ["节点类型", node_type],
        ["节点名称", node_name],
        ["节点id", node_id],
    ];
    if (node_type === "Report") {
        if ($("#download_btn").length === 0) {
            $("<button id='download_btn' onclick=download(node_id)>下载报告</button>").insertAfter($("#sheet1"))
            $("#download_btn").show();
        } else {
            $("#download_btn").show();
        }
    } else {
        $("#download_btn").hide();
    }
    $("#sheet1").show();
    updateGridJsData(sheet_data, "sheet1");

    if (node_type == "SearchText") {
        $("#sheet2").hide();
        if ($("#extras_field_p").length !== 0) {
            $("#extras_field_p").text("");
        }
        return;
    }
    // 获取节点对象数据
    let node_info = getNodeInfo(node_type, node_id);
    let keys = Object.keys(node_info);
    let data = [];
    for (let i = 0; i < keys.length; i++) {
        let k = keys[i];
        if (k === "extras_field") {
            if ($("#extras_field_p").length !== 0) {
                $("#extras_field_p").text("备注信息：" + node_info[k]);
            } else {
                $("#editor").append("<p id='extras_field_p'>备注信息：" + node_info[k] + "</p>");
            }
            continue;
        }
        let v = node_info[k];
        data.push([k, v]);
    }
    let sheet2_data = {
        // columns: ["item", "值"],
        sort: true,
        pagination: true,
        data: data,
    };
    $("#sheet2").show();
    updateGridJsData(sheet2_data, "sheet2")
}

function graph_node_clicked_with_control(graph_node) {
    console.log("...")
    if (graph_node.type === "Station") { // ctrl+单机 : 查找电厂所有的报告
        let reports = getReportOfStation(graph_node.id);
        let nodes = [{name: graph_node.name, id: graph_node.id, type: graph_node.type}];
        let edges = [];
        for (let i = 0; i < reports.length; i++) {
            nodes.push({name: reports[i].Title1, id: reports[i].Id, type: "Report"})
            edges.push({source: 0, target: i + 1, relation: "", value: 1},);
        }
        renderForceGraph(nodes, edges);
        $("#svg").prev("p").remove();
    } else if (graph_node.type === "Report") {// ctrl+单机 : 查找报告中所有的电厂
        let stations = getStationOfReport(graph_node.id);
        let nodes = [{name: graph_node.name, id: graph_node.id, type: graph_node.type}];
        let edges = [];
        for (let i = 0; i < stations.length; i++) {
            nodes.push({name: stations[i].Name, id: stations[i].Id, type: "Station"})
            edges.push({source: 0, target: i + 1, relation: "", value: 1},);
        }
        renderForceGraph(nodes, edges);
        $("#svg").prev("p").remove();
    }

}

function getStationOfReport(report_id) {
    let url = baseUrl + "?Action=SearchStationOfReport&Value=" + report_id;
    var stations;
    loadData(url, function (data) {
        stations = data;
    }, report_id, false);
    return stations;
}

function getReportOfStation(station_id) {
    /**
     * 根据电站id查找电站相关的所有报告
     *
     * @type {string}
     */
    let url = baseUrl + "?Action=SearchReportOfStation&Value=" + station_id;
    var reports;
    loadData(url, function (data) {
        for (let i = 0; i < data.length; i++) {
            reports = data;
        }
    }, station_id, false);
    return reports;
}

function download(reportId) {
    let url = baseUrl + "download/" + reportId.split("/")[1];
    // window.location.href = url;
    window.open(url);
}

function showResult(data, in_data) {
    console.log("渲染查询结果...")
    try {
        // ======================== 拿到服务器返回的搜索结果 ====================================
        let return_type;
        for (let i = 0; i < Object.keys(data).length; i++) {
            let currentType = Object.keys(data)[i]
            if (["Report", "Station", "Group", "Test"].indexOf(currentType) !== -1) {
                return_type = currentType;
                break;
            }
        }
        let graph_node_name;
        let graph_node_id = "id";
        let graph_node_type = return_type;
        if (return_type === "Report") {
            graph_node_name = "title1";
        } else if (return_type === "Station") {
            graph_node_name = "name";
        } else if (return_type === "Group") {
            // Group的名称需要特殊处理，这里不用操作
        } else if (return_type === "Test") {
            graph_node_name = "name";
        }
        let return_result_nodes = $.parseJSON(data[return_type]);
        // ======================== 拿到服务器返回的搜索结果 ====================================

        // ======================== 组装用于d3显示的节点和边 ====================================
        let searchValue;
        // 判断是多条件搜索还是单条件搜索
        if ($(".searchCondition").length > 1) {
            searchValue = "[多条件检索]"
        } else {
            searchValue = $(".searchValue").val();
        }
        let nodes = [{name: searchValue, id: null, type: "SearchText"}];
        let edges = [];
        for (let i = 0; i < return_result_nodes.length; i++) {
            let current = return_result_nodes[i];
            if (return_type === "Group") {
                nodes.push({
                    name: getStationNameById(current["station_id"]) + "#" + current["no"],
                    id: current[graph_node_id],
                    type: graph_node_type
                })
            } else {
                nodes.push({name: current[graph_node_name], id: current[graph_node_id], type: graph_node_type});
            }
            edges.push({source: 0, target: i + 1, relation: "", value: 1},);
        }
        // ======================== 组装用于d3显示的节点和边 ====================================
        renderForceGraph(nodes, edges);
        $("#svg").prev("p").remove();
    } catch (e) {
        console.log("异常类型：" + e.name);
        console.log("异常信息：" + e.message);
        $("#svg").prevAll("p").remove();
        let currentTime = new Date();
        $("<p>" + currentTime.getFullYear() + "-" + currentTime.getMonth() + "-" + currentTime.getDate() + " "
            + currentTime.getHours() + ":" + currentTime.getMinutes() + ":" + currentTime.getSeconds()
            + " " + JSON.stringify(data) + "</p>").insertBefore($("#svg"));
    }

}

function getStationNameById(id) {
    /**
     * 根据电厂id查询电厂名称
     * @type {{No0: {type2: string, type1: string, value: [*]}}}
     */
    let sendValue = {"No0": {"type1": "电厂", "type2": "电厂id", "value": [id]}}
    sendValue = JSON.stringify(sendValue);
    let url = baseUrl + "?Action=Search&Value=" + sendValue;
    var station_name;
    loadData(url, function (data) {
        let station = $.parseJSON(data["Station"])[0];
        station_name = station["name"];
    }, sendValue, false);
    return station_name;
}

function getNodeInfo(type, id) {
    /**
     * 获取节点信息，这里的节点是d3的力图节点
     * @type: 图节点的类型
     * @id: 图节点对应的元素的id
     */
    let sendValue;
    if (type === "Station") {
        sendValue = JSON.stringify({"No0": {"type1": "电厂", "type2": "电厂id", "value": [id]}});
    } else if (type === "Report") {
        sendValue = JSON.stringify({"No0": {"type1": "报告", "type2": "报告编号", "value": [id]}});
    } else if (type === "Group") {
        sendValue = JSON.stringify({"No0": {"type1": "机组", "type2": "机组id", "value": [id]}});
    } else if (type === "Test") {
        sendValue = JSON.stringify({"No0": {"type1": "试验", "type2": "试验id", "value": [id]}});
    }
    let url = baseUrl + "?Action=Search&Value=" + sendValue;
    var node_info;
    loadData(url, function (data) {
        node_info = $.parseJSON(data[type])[0];
    }, sendValue, false);
    return node_info;
}

var searchInfo = {
    "报告": ["报告编号", "报告标题", "报告类型", "报告日期", "报告日期范围", "其他"],
    "电厂": ["电厂名称", "位置", "建设时间", "投产时间", "大修时间", "电厂id", "其他"],
    "机组": ["机组型号", "机组型式", "加热器级数", "给水泵驱动方式",
        "额定出力", "额定出力范围",

        "主蒸汽压力", "主蒸汽压力范围",
        "主蒸汽温度", "主蒸汽温度范围",
        "汽机生产厂家", "汽机改造厂家", "机组id",
        "其他"],
    "试验": ["项目负责人", "参与人员", "试验时间", "试验时间范围", "试验id", "其他"]
}


function search() {
    /**
     * 当导航栏的下拉列表改变或下拉列表对应的文字改变时，触发该方法，进行查询，查询结果通过loadData方法调用showResult进行显示
     */
    console.log("查询中...")
    let conditions = $(".searchCondition");
    let type1;
    let type2;
    let value;
    let sendValue = {}; // 向后台发送的查询字典
    for (let i = 0; i < conditions.length; i++) {
        condition_div = conditions[i];
        let valueEle = $(condition_div).children();
        type1 = $(valueEle[0]).find("option:selected").text();
        type2 = $(valueEle[1]).find("option:selected").text();
        if (valueEle.length === 3) {
            let inputType = $(valueEle[2]).attr("type");
            if (inputType === "text" || inputType === "date") {
                value = [$(valueEle[2]).val()];
            } else {
                value = [null];
            }

        } else if (valueEle.length === 6) {
            let inputType = $(valueEle[3]).attr("type");
            if (inputType === "text" || inputType === "date") {
                value = [$(valueEle[3]).val(), $(valueEle[5]).val()];
            } else {
                value = [null, null];
            }

        }
        sendValue["No" + i] = {"type1": type1, "type2": type2, "value": value}
    }
    sendValue = JSON.stringify(sendValue); // 将js的字典对象（即json对象）转换为字符串
    let url = baseUrl + "?Action=Search&Value=" + sendValue;
    loadData(url, showResult, sendValue);
}


function insertSearchCondition() {
    /**
     * 插入新的查询条件输入框
     */
    $('    <div class="searchCondition" style="margin: 10px; border: 2px solid crimson; padding-bottom: 3px">\n' +
        '        <select class="searchType" onchange="changeSecond(this)">\n' +
        '            <option value="report">报告</option>\n' +
        '            <option value="station">电厂</option>\n' +
        '            <option value="group">机组</option>\n' +
        '            <option value="test">试验</option>\n' +
        '        </select>\n' +
        '        <select class="searchTypeSecond" onchange="changeValueType(this)">\n' +
        '            <option value="id">报告编号</option>\n' +
        '            <option value="title">报告标题</option>\n' +
        '            <option value="type">报告类型</option>\n' +
        '            <option value="datetime">报告日期</option>\n' +
        '            <option value="datetime">报告日期范围</option>\n' +
        '            <option value="datetime">其他</option>\n' +
        '        </select>\n' +
        '        <input type="text" class="searchValue" value="华能" onchange="search()">\n' +
        '    </div>').insertBefore($("#insertSearchCondition"));
}

function changeSecond(firstSelect) {
    /**
     * 根据一级菜单改变二级菜单
     */
    let first = $(firstSelect);
    let selectedText = first.find("option:selected").text();
    let secondItems = searchInfo[selectedText];
    let second = first.next("select")[0];
    second.options.length = 0;
    for (let item in secondItems) { // 需要注意的是，这里取到的item是索引，不是值，js这一点有点奇怪
        second.options.add(new Option(secondItems[item], item));
    }
    changeValueType(second)
}

function changeValueType(secondSelect) {
    /**
     * 根据查询类型更改数值显示区域的类型，如范围都显示为两个输入框
     */
    function insertField(type, ele, num) {
        /**
         * 在ele元素后添加num个type类型的输入框
         */
        if (num === 1) {
            $("<input type='" + type + "' class='searchValue'>").insertAfter(ele);
        } else if (num === 2) {
            $("<label style='display: block; margin: 10px 0 0 0'>最小值：</label><input type='"
                + type + "' class='searchValue1'>" +
                "<label style='display: block'>最大值：</label>" +
                "<input type='" + type + "' class='searchValue2'>").insertAfter(ele);
        }
    }

    let second = $(secondSelect);
    let selectedText = second.find("option:selected").text();
    second.nextAll().remove();
    let num;
    if (selectedText.indexOf("范围") > 0) {  // 如果搜索条件是范围，则需要两个输入域
        num = 2;
    } else {
        num = 1;
    }
    if (selectedText.indexOf("日期") > 0 || selectedText.indexOf("时间") > 0) {  // 如果搜索条件是时间
        insertField("date", second, num); // 在second元素后插入num个日期框
    } else {
        insertField("text", second, num); // 在second元素后插入num个文本框
    }
}